// 

#ifndef __MUTEX_H__
#define __MUTEX_H__

#include <pthread.h>
#include "sf_assert.h"
#include <time.h>

// Customize this to be a mutex on your platform

class SFCond;
class SFMutex
{
    friend class SFCond;
public:
    SFMutex()
    {
        SF_ASSERT(0 == pthread_mutex_init(&mMutex, NULL));
    }
    ~SFMutex()
    {
        pthread_mutex_destroy(&mMutex);
    }
    
    void lock()
    {
        pthread_mutex_lock(&mMutex);
    }
    void unlock()
    {
        pthread_mutex_unlock(&mMutex);
    }
    
    bool tryLock()
    {
        return pthread_mutex_trylock(&mMutex) ? false : true;
    }
    bool tryLock(int msec)
    {
#if !defined (__MACH__) && !defined (__APPLE__)
        struct timespec tv;
        clock_gettime(CLOCK_REALTIME, &tv);

        long long tmp = tv.tv_nsec + (long long)msec * 1000000LL;

        tv.tv_sec  += tmp / 1000000000L;
        tv.tv_nsec  = tmp % 1000000000L;

        return pthread_mutex_timedlock(&mMutex, &tv) ? false : true;
#else
        (void) msec;
        return false;
#endif
    }
private:
    pthread_mutex_t mMutex;
};

class SFCond
{
public:
    SFCond()
    {
#if !defined (__MACH__) && !defined (__APPLE__)
        pthread_condattr_t condattr;
        pthread_condattr_init(&condattr);
        pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);

        SF_ASSERT(0 == pthread_cond_init(&mCond, &condattr));
        pthread_condattr_destroy(&condattr);
#else
        pthread_cond_init(&mCond, NULL);
#endif
    }

    void wait(SFMutex &mtx, int msec)
    {
        struct timespec tv;        
        clock_gettime(CLOCK_MONOTONIC, &tv);

        long long tmp = tv.tv_nsec + (long long)msec * 1000000LL;

        tv.tv_sec  += tmp / 1000000000L;
        tv.tv_nsec  = tmp % 1000000000L;

        mtx.lock();

        pthread_cond_timedwait(&mCond, &mtx.mMutex, &tv);
        mtx.unlock();
    }

    void wait(SFMutex &mtx)
    {
        mtx.lock();
        pthread_cond_wait(&mCond, &mtx.mMutex);
        mtx.unlock();
    }
    void signal(SFMutex &mtx)
    {
        mtx.lock();
        pthread_cond_signal(&mCond);
        mtx.unlock();
    }

    void broadcast(SFMutex &mtx)
    {
        mtx.lock();
        pthread_cond_broadcast(&mCond);
        mtx.unlock();
    }

    // only operate
    int waitSimple(SFMutex &mtx, int msec)
    {
        struct timespec tv;
        clock_gettime(CLOCK_MONOTONIC, &tv);

        long long tmp = tv.tv_nsec + (long long)msec * 1000000LL;

        tv.tv_sec  += tmp / 1000000000L;
        tv.tv_nsec  = tmp % 1000000000L;

        return pthread_cond_timedwait(&mCond, &mtx.mMutex, &tv);
    }

    int waitSimple(SFMutex &mtx)
    {
        return pthread_cond_wait(&mCond, &mtx.mMutex);
    }
    void signal()
    {
        pthread_cond_signal(&mCond);
    }

    void broadcast()
    {
        pthread_cond_broadcast(&mCond);
    }

private:
    pthread_cond_t mCond;
};

class Autolock
{
public:
    Autolock(SFMutex* mutex) : mMutex(mutex)
    {
        mutex->lock();
    }
    ~Autolock()
    {
        mMutex->unlock();
    }
    
private:
    SFMutex* mMutex;
};

class CondWait
{
public:
    CondWait()
    {
#if !defined (__MACH__) && !defined (__APPLE__)
        pthread_condattr_t condattr;
        pthread_condattr_init(&condattr);
        pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);

        pthread_mutex_init(&mMutex, NULL);
        pthread_cond_init(&mCond, &condattr);
        pthread_condattr_destroy(&condattr);
#else
        pthread_mutex_init(&mMutex, NULL);
        pthread_cond_init(&mCond, NULL);
#endif
    }
    ~CondWait()
    {
        pthread_cond_signal(&mCond);
        pthread_mutex_unlock(&mMutex);

        pthread_mutex_destroy(&mMutex);
        pthread_cond_destroy(&mCond);
    }

    void signal()
    {
        pthread_mutex_lock(&mMutex);
        pthread_cond_signal(&mCond);
        pthread_mutex_unlock(&mMutex);
    }

    void broadcast()
    {   
        pthread_mutex_lock(&mMutex);
        pthread_cond_broadcast(&mCond);
        pthread_mutex_unlock(&mMutex);
    }

    int wait(int msec)
    {
        struct timespec tv;
        clock_gettime(CLOCK_MONOTONIC, &tv);

        long long tmp = tv.tv_nsec + (long long)msec * 1000000LL;

        tv.tv_sec  += tmp / 1000000000L;
        tv.tv_nsec  = tmp % 1000000000L;

        pthread_mutex_lock(&mMutex);
        int ret = pthread_cond_timedwait(&mCond, &mMutex, &tv);
        pthread_mutex_unlock(&mMutex);

        return ret;
    }
    template<typename Functer>
    int wait(int msec, Functer func)
    {
        struct timespec tv;
        clock_gettime(CLOCK_MONOTONIC, &tv);

        long long tmp = tv.tv_nsec + (long long)msec * 1000000LL;

        tv.tv_sec  += tmp / 1000000000L;
        tv.tv_nsec  = tmp % 1000000000L;

        pthread_mutex_lock(&mMutex);
        func();
        int ret = pthread_cond_timedwait(&mCond, &mMutex, &tv);
        pthread_mutex_unlock(&mMutex);

        return ret;
    }
    int wait()
    {
        pthread_mutex_lock(&mMutex);
        int ret = pthread_cond_wait(&mCond, &mMutex);
        pthread_mutex_unlock(&mMutex);
        return ret;
    }

    template<typename Functer>
    int wait(Functer func)
    {
        pthread_mutex_lock(&mMutex);
        func();
        int ret = pthread_cond_wait(&mCond, &mMutex);
        pthread_mutex_unlock(&mMutex);
        return ret;
    }
private:
    pthread_mutex_t mMutex;
    pthread_cond_t  mCond;
};

#endif // __MUTEX_H__
